package LiuYun

import spinal.core._
import spinal.lib._

class Forwarding extends Bundle{
    val valid:Bool = Bool()
    val block:Bool = Bool()
    val grwr :Bool = Bool()
    val dest :UInt = UInt(5 bits)
    val value:UInt = LISA.GPR
    def hit(src:UInt):Bool = valid && grwr && dest === src
}
class DeCsr extends Bundle{
    val is_kernel:Bool = Bool()
    val alert    :Bool = Bool()
    def collect(csr:Csr){
        is_kernel := csr.crmd.plv === U(0)
        alert     := csr.crmd.ie && (csr.ecfg.lie & csr.estat.getIS).orR
    }
}

class StageDe extends Component{
    val io = new Bundle{
        val ic    :PipeCtrl = slave(new PipeCtrl)
        val idat  :SDatDe   = in  (new SDatDe)
        val oc    :PipeCtrl = master(new PipeCtrl)
        val odat  :SDatEx1  = out (new SDatEx1)
        val brbus :BrBus    = in  (new BrBus)
        val exbus :CoreCancel = in  (new CoreCancel)
        val r     :Vec[RFRead] = Vec(master(new RFRead),2)
        val fw    :Vec[Forwarding]  = in(Vec(new Forwarding,4))
        val csr   :DeCsr   = in(new DeCsr)
    }
    val cancel:Bool = io.exbus.cancel //|| io.brbus.cancel
    val data:SDatEx1   = new SDatEx1 assignedFrom io.idat
    val pipe:StageBuffer[SDatEx1] = new StageBuffer[SDatEx1](data)
    pipe.update(io.ic,io.oc,cancel)
    io.odat := pipe.data

    val info:InstInfo = new InstInfo
    val decoder:InstDecoder = new InstDecoder
    decoder.inst <> data.inst
    decoder.info <> info

    io.r(0).addr := info.rj
    io.r(1).addr := Mux(info.use_rd, info.rd, info.rk)
    // matrix for forward checking
    val hits_m:Vec[Bits] = Vec(Array(info.rd,info.rj,info.rk).map((gr:UInt) => Vec(io.fw.map(_.hit(gr))).asBits))
    val hits:Vec[Bits] = Vec(Array(hits_m(1),MuxOH.or(Array(info.use_rd,info.use_rk),Array(hits_m(0),hits_m(2)))))
    val block:Vec[Bool] = Vec(Bool()  ,2)
    val value:Vec[UInt] = Vec(LISA.GPR,2)
    for(i <- Range(0,2)){
        block(i) := False
        value(i) := io.r(i).data
        for(j <- Range(3,-1,-1)){
            when(hits(i)(j)){
                block(i) := io.fw(j).block
                value(i) := io.fw(j).value
            }
        }
    }
    data.a := value(0)
    data.b := value(1)
    data.grwr := info.grwr && info.dest.orR && !pipe.hasex
    data.dest := info.dest
    data.itype := info.itype
    data.subcode := info.subcode
    val check_plv :Boolean = false
    val trap_plv  :Bool = if(check_plv) info.type_plv && !io.csr.is_kernel else False
    val trap_ine  :Bool = !info.valid || info.itype.is_invtlb && io.idat.inst(4 downto 0) >= U(7)
    val trap_any  :Bool = info.trap_sys || info.trap_brk || trap_plv || trap_ine
    val trap_ecode:UInt = ( Mux(info.trap_sys,LISA.Ex.SYS.code,U(0))
                          | Mux(info.trap_brk,LISA.Ex.BRK.code,U(0))
                          | Mux(     trap_ine,LISA.Ex.INE.code,U(0))
                          | Mux(     trap_plv,LISA.Ex.IPE.code,U(0))
                          )
    when(!io.ic.ex && trap_any){
        data.ecode := trap_ecode
    }
    when(io.csr.alert){
        data.ecode := U(0)
    }
    val block_me:Bool = RegInit(False)
    when(cancel){
        block_me := False
    }.elsewhen(pipe.go){
        block_me := data.itype.is_ertn
    }
    pipe.newex := io.ic.valid && (trap_any || io.csr.alert)
    pipe.ready := !block.asBits.orR && !block_me
}

